// ignore_for_file: non_constant_identifier_names

import 'dart:io';

import 'package:dart_bean/cass_info.dart';
import 'package:dart_bean/field_info.dart';
import 'package:dart_bean/generator.dart';
import 'package:dart_bean/setting.dart';
import 'package:dart_bean/util/ext.dart';

class PartGenerator {
  static void start(Iterable<CassInfo> singleFileClassList) {
    final dartFile = singleFileClassList.first.file;
    final m = Generator.getGenPartMaterial(dartFile);
    m.tasks.addAll(singleFileClassList.map((e) => _GenCodeTaskForBeanPart(e)));

    final partFile = getPartFile(Generator.libDir, Generator.genDir, dartFile);
    PartGenerator().gen(dartFile, partFile, m);
  }

  void gen(File dartFile, File partFile, GenCodeMaterial material) {
    final comment = """
//ignore_for_file: no_leading_underscores_for_local_identifiers
// This file is automatically generated. DO NOT EDIT, all your changes would be lost.
""";
    final sb = StringBuffer();
    sb.writeln(comment);
    sb.writeln("part of '${genImportPath(dartFile, partFile)}';");
    final tasks = material.tasks;
    final Set<GenCodeTask> doneTasks = {};
    while (tasks.isNotEmpty) {
      final task = tasks.first;
      task.gen(material);
      doneTasks.add(task);
      tasks.removeAll(doneTasks);
    }

    for (var f in material.functions) {
      sb.writeln(f);
    }
    for (var c in material.consts) {
      sb.writeln(c);
    }
    ensureParent(partFile);
    partFile.writeAsStringSync(safeDartFormatter(sb.toString()));
  }
}

class GenCodeMaterial {
  final Set<GenCodeTask> tasks = {};
  final Set<String> consts = {};
  final Set<String> functions = {};
}

abstract class GenCodeTask {
  void gen(GenCodeMaterial output);
}

class _GenCodeTaskForBeanPart extends GenCodeTask {
  final CassInfo cassInfo;

  _GenCodeTaskForBeanPart(this.cassInfo);

  @override
  void gen(GenCodeMaterial output) {
    output.functions.add(_genFromJson(cassInfo));
    output.functions.add(_genToJson(cassInfo));
  }

  String _genFromJson(CassInfo c) {
    final className = c.name;
    final instance = getFromJsonInstanceName(c);
    final fields = c.fields.map((e) => _genFieldDeserialize(e, instance)).where((e) => e.isNotEmpty).toList();
    // fields.sort((a, b) => a.length.compareTo(b.length));
    final fieldsCode = fields.join('\n');
    return """
    $className ${getFromJsonMethodName(c)}(Map<String, dynamic> $fromJsonParamName) {
      final $instance = $className();	
      $fieldsCode
      return $instance;
    }
""";
  }

  String _genFieldDeserialize(FieldInfo f, String instance) {
    if (f.isIgnoreDeserialize) return '';

    final fieldName = f.name;
    final convert = f.getConvert();
    return "$instance.$fieldName = $convert ?? $instance.$fieldName;";
  }

  String _genToJson(CassInfo c) {
    final instanceName = toJsonParamName;
    final className = c.name;
    final fields = c.fields.map((e) => _genFieldSerialize(e, instanceName)).where((e) => e.isNotEmpty).toList();
    // fields.sort((a, b) => a.length.compareTo(b.length));
    final fieldsCode = fields.join('\n');
    return """
    Map<String, dynamic> ${getToJsonMethodName(c)}($className $instanceName)=> {     
      $fieldsCode
    };
""";
  }

  String _genFieldSerialize(FieldInfo f, String instanceName) {
    if (f.isIgnoreSerialize) return '';
    final fieldName = f.name;
    final instance = instanceName;
    return "'${f.jsonName}': ${f.toJsonRightValue('$instance.$fieldName')},";
  }

  late final _hashCode = '${cassInfo.file.path}:${cassInfo.name}'.hashCode;

  @override
  int get hashCode => _hashCode;

  @override
  bool operator ==(Object other) {
    return other is _GenCodeTaskForBeanPart && other.hashCode == hashCode;
  }

  @override
  String toString() {
    return 'GenCodeTaskForBeanPart:${getRelativePath(cassInfo.file, Generator.libDir)}:${cassInfo.name}';
  }
}
